﻿using System.Linq;
using CID.Domain.Audit;
using CID.Domain.Development;
using CID.Domain.Infrastructure;
using CID.Domain.Operations;
using CID.Domain.Users;
using CID.Tests.Support;
using NailsFramework.Config;
using NHibernate;

namespace CID.Web.Tests
{
    abstract class BaseWebTests : BasePersistenceTests
    {
        #region DomainData Repositories
        protected class UsersDataRepository
        {
            public IUser User1 { get; private set; }
            public IUser User2 { get; private set; }

            public UsersDataRepository(IUser user1, IUser user2)
            {
                User1 = user1;
                User2 = user2;
            }
        }

        protected sealed class OperationsDataRepository
        {
            public IServerRole ServerRole1 { get; private set; }
            public IServerRole ServerRole2 { get; private set; }
            public IServer Server1 { get; private set; }
            public IServer Server2 { get; private set; }
            public IEnvironment Environment1 { get; private set; }
            public IEnvironment Environment2 { get; private set; }
            public ILocation Location1 { get; private set; }
            public ILocation Location2 { get; private set; }

            public OperationsDataRepository(IServerRole serverRole1, IServerRole serverRole2, IServer server1, IServer server2,
                IEnvironment environment1, IEnvironment environment2, ILocation location1, ILocation location2)
            {
                ServerRole1 = serverRole1;
                ServerRole2 = serverRole2;
                Server1 = server1;
                Server2 = server2;
                Environment1 = environment1;
                Environment2 = environment2;
                Location1 = location1;
                Location2 = location2;
            }

        }

        protected sealed class InfrastructureDataRepository
        {
            public DeploymentAgent DeploymentAgent1 { get; private set; }
            public DeploymentAgent DeploymentAgent2 { get; private set; }
            public PackageType PackageType { get; private set; }
            public TemplateType TemplateType { get; private set; }
            public IActivationTemplate ActivationTemplate { get; private set; }
            public IDeactivationTemplate DeactivationTemplate { get; private set; }
            public IDeploymentTemplate DeploymentTemplate { get; private set; }
            public IParameter ActivationTemplateBooleanParameter { get; private set; }
            public IParameter ActivationTemplateDateTimeParameter { get; private set; }
            public IParameter DeactivationTemplateDecimalParameter { get; private set; }
            public IParameter DeactivationTemplateIntegerParameter { get; private set; }
            public IParameter DeploymentTemplateStringParameter { get; private set; }
            public IParameter DeploymentTemplateUniqueIdentifierParameter { get; private set; }


            public InfrastructureDataRepository(DeploymentAgent deploymentAgent1, DeploymentAgent deploymentAgent2)
            {
                DeploymentAgent1 = deploymentAgent1;
                DeploymentAgent2 = deploymentAgent2;
            }

            public InfrastructureDataRepository(DeploymentAgent deploymentAgent1, DeploymentAgent deploymentAgent2, PackageType packageType, TemplateType templateType,
                IActivationTemplate activationTemplate, IDeactivationTemplate deactivationTemplate, IDeploymentTemplate deploymentTemplate,
                IParameter activationTemplateBooleanParameter, IParameter activationTemplateDateTimeParameter, IParameter deactivationTemplateDecimalParameter,
                IParameter deactivationTemplateIntegerParameter, IParameter deploymentTemplateStringParameter, IParameter deploymentTemplateUniqueIdentifierParameter)
                : this(deploymentAgent1, deploymentAgent2)
            {
                PackageType = packageType;
                TemplateType = templateType;
                ActivationTemplate = activationTemplate;
                DeactivationTemplate = deactivationTemplate;
                DeploymentTemplate = deploymentTemplate;
                ActivationTemplateBooleanParameter = activationTemplateBooleanParameter;
                ActivationTemplateDateTimeParameter = activationTemplateDateTimeParameter;
                DeactivationTemplateDecimalParameter = deactivationTemplateDecimalParameter;
                DeactivationTemplateIntegerParameter = deactivationTemplateIntegerParameter;
                DeploymentTemplateStringParameter = deploymentTemplateStringParameter;
                DeploymentTemplateUniqueIdentifierParameter = deploymentTemplateUniqueIdentifierParameter;
            }
        }

        protected sealed class DevelopmentDataRepository
        {
            public Component Component1 { get; private set; }
            public Component Component2 { get; private set; }
            public Version Component1UnreleasedVersion { get; private set; }
            public Version Component2UnreleasedVersion { get; private set; }
            public Release Release1 { get; private set; }
            public Release Release2 { get; private set; }

            public DevelopmentDataRepository(Component component1, Component component2, Release release1, Release release2)
            {
                Component1 = component1;
                Component2 = component2;
                Release1 = release1;
                Release2 = release2;
                Component1UnreleasedVersion = (Version)Component1.Versions.First(x => (x.Release == null));
                Component2UnreleasedVersion = (Version)Component2.Versions.First(x => (x.Release == null));
            }

        }

        protected sealed class AuditDataRepository
        {
            public ReleaseDeploymentLog ReleaseDeploymentLog1 { get; private set; }
            public ReleaseDeploymentLog ReleaseDeploymentLog2 { get; private set; }
            public ReleaseDeploymentLog ReleaseDeploymentLog3 { get; private set; }

            public AuditDataRepository(ReleaseDeploymentLog releaseDeploymentLog1, ReleaseDeploymentLog releaseDeploymentLog2, ReleaseDeploymentLog releaseDeploymentLog3)
            {
                ReleaseDeploymentLog1 = releaseDeploymentLog1;
                ReleaseDeploymentLog2 = releaseDeploymentLog2;
                ReleaseDeploymentLog3 = releaseDeploymentLog3;
            }
        }
        #endregion

        protected static UsersDataRepository UsersData { get; private set; }
        protected static OperationsDataRepository OperationsData { get; private set; }
        protected static InfrastructureDataRepository InfrastructureData { get; private set; }
        protected static DevelopmentDataRepository DevelopmentData { get; private set; }
        protected static AuditDataRepository AuditData { get; private set; }

        private static InfrastructureDataRepository infrastractureDataPartial;

        protected override void SetupData()
        {
            base.SetupData();
            var session = OpenSession();

            CreateUsersData(session);

            CreateInfrastractureDataPartial(session);

            CreateOperationsData(session);

            CreateInfrastructureData(session);

            CreateDevelopmentData(session);

            CreateAuditData(session);

            CleanSession(session);
        }

        private void CreateUsersData(ISession session)
        {
            var user1 = new User("User One", "user.one", "user.one@somedomain.com");
            user1.AssignRole(UserRole.Architect);
            user1.AssignRole(UserRole.ComponentManager);
            session.SaveOrUpdate(user1);
            var user2 = new User("User Two", "user.two", "user.two@somedomain.com");
            user2.AssignRole(UserRole.Manager);
            user2.AssignRole(UserRole.ReleaseManager);
            session.SaveOrUpdate(user2);

            UsersData = new UsersDataRepository(user1, user2);
        }

        private void CreateInfrastractureDataPartial(ISession session)
        {
            var deploymentAgent1 = new DeploymentAgent("Deployment Agent One", "Deployment Agent One Address");
            session.SaveOrUpdate(deploymentAgent1);
            var deploymentAgent2 = new DeploymentAgent("Deployment Agent Two", "Deployment Agent Two Address");
            session.SaveOrUpdate(deploymentAgent2);

            infrastractureDataPartial = new InfrastructureDataRepository(deploymentAgent1, deploymentAgent2);
        }

        private void CreateOperationsData(ISession session)
        {
            var serverRole1 = new ServerRole("Server Role One", "Server Role Description One");
            session.SaveOrUpdate(serverRole1);
            var serverRole2 = new ServerRole("Server Role Two", "Server Role Description Two");
            session.SaveOrUpdate(serverRole2);

            var location1 = new Location("Location One", "Location One Description", infrastractureDataPartial.DeploymentAgent1);
            session.SaveOrUpdate(location1);
            var location2 = new Location("Location Two", "Location Two Description", infrastractureDataPartial.DeploymentAgent2);
            session.SaveOrUpdate(location2);

            var server1 = new Server("Server One", "Server One Address", location1);
            session.SaveOrUpdate(server1);
            var server2 = new Server("Server Two", "Server Two Address", location2);
            session.SaveOrUpdate(server2);

            var environment1 = new Environment("Environment One", "Environment One Description", UsersData.User1);
            session.SaveOrUpdate(environment1);
            var environment2 = new Environment("Environment Two", "Environment Two Description", UsersData.User2);
            session.SaveOrUpdate(environment2);

            OperationsData = new OperationsDataRepository(serverRole1, serverRole2, server1, server2, environment1, environment2, location1, location2);
        }

        private void CreateInfrastructureData(ISession session)
        {
            var deploymentAgent1 = infrastractureDataPartial.DeploymentAgent1;
            var deploymentAgent2 = infrastractureDataPartial.DeploymentAgent2;

            var packageType = new PackageType("Package Type", "Package Type Description");
            session.SaveOrUpdate(packageType);

            var templateType = new TemplateType("Template Type", "Template Type Description", "ContentEditorConverter", "Editor View Name");
            session.SaveOrUpdate(templateType);

            var activationTemplate = templateType.CreateActivationTemplate("Activation Template", "Activation Template Description", new byte[] { 1, 2, 3 });
            var activationTemplateBooleanParameter = activationTemplate.CreateParameter("Activation Parameter One Name", "Activation Parameter One Description", ParameterType.Boolean);
            var activationTemplateDateTimeParameter = activationTemplate.CreateParameter("Activation Parameter Two Name", "Activation Parameter Two Description", ParameterType.DateTime);
            session.SaveOrUpdate(activationTemplate);
            var deactivationTemplate = templateType.CreateDeactivationTemplate("Deactivation Template", "Deactivation Template Description", new byte[] { 1, 2, 3 });
            var deactivationTemplateDecimalParameter = deactivationTemplate.CreateParameter("Deactivation Parameter One Name", "Deactivation Parameter One Description", ParameterType.Decimal);
            var deactivationTemplateIntegerParameter = deactivationTemplate.CreateParameter("Deactivation Parameter Two Name", "Deactivation Parameter Two Description", ParameterType.Integer);
            session.SaveOrUpdate(deactivationTemplate);
            var deploymentTemplate = templateType.CreateDeploymentTemplate("Deployment Template", "Deployment Template Description", new byte[] { 1, 2, 3 });
            var deploymentTemplateStringParameter = deploymentTemplate.CreateParameter("Deployment Parameter One Name", "Deployment Parameter One Description", ParameterType.String);
            var deploymentTemplateUniqueIdentifierParameter = deploymentTemplate.CreateParameter("Deployment Parameter Two Name", "Deployment Parameter Two Description", ParameterType.UniqueIdentifier);
            session.SaveOrUpdate(deploymentTemplate);

            InfrastructureData = new InfrastructureDataRepository(deploymentAgent1, deploymentAgent2, packageType, templateType, activationTemplate, deactivationTemplate, deploymentTemplate,
                activationTemplateBooleanParameter, activationTemplateDateTimeParameter, deactivationTemplateDecimalParameter, deactivationTemplateIntegerParameter,
                deploymentTemplateStringParameter, deploymentTemplateUniqueIdentifierParameter);
        }

        private void CreateDevelopmentData(ISession session)
        {
            var component1 = new Component("Component One Name", "Component One Description", OperationsData.ServerRole1, UsersData.User1);
            SetUpDefinition(component1);
            var component1version1 = component1.CreateVersion("Component One Version One Name", "Component One Version One Description");
            SetUpDefinition(component1version1);
            var component1version2 = component1.CreateVersion("Component One Version Two Name", "Component One Version Two Description");
            SetUpDefinition(component1version2);
            var component1version3 = component1.CreateVersion("Component One Version Three Name", "Component One Version Three Description");
            SetUpDefinition(component1version3);
            session.SaveOrUpdate(component1);

            var component2 = new Component("Component Two Name", "Component Two Description", OperationsData.ServerRole2, UsersData.User1);
            SetUpDefinition(component2);
            var component2version1 = component2.CreateVersion("Component Two Version One Name", "Component Two Version One Description");
            SetUpDefinition(component2version1);
            var component2version2 = component2.CreateVersion("Component Two Version Two Name", "Component Two Version Two Description");
            SetUpDefinition(component2version2);
            var component2version3 = component2.CreateVersion("Component Two Version Three Name", "Component Two Version Three Description");
            SetUpDefinition(component2version3);
            session.SaveOrUpdate(component2);

            var release1 = new Release("Release One Name", "Release One Description", UsersData.User2);
            release1.SetActive();
            component1version1.SetRelease(release1);
            component2version1.SetRelease(release1);
            session.SaveOrUpdate(release1);
            var release2 = new Release("Release Two Name", "Release Two Description", UsersData.User2);
            release2.SetActive();
            component1version2.SetRelease(release2);
            component2version2.SetRelease(release2);
            session.SaveOrUpdate(release2);

            DevelopmentData = new DevelopmentDataRepository(component1, component2, release1, release2);
        }

        private void CreateAuditData(ISession session)
        {
            var releaseDeploymentLog1 = new ReleaseDeploymentLog(DevelopmentData.Release1, OperationsData.Environment1, UsersData.User1);
            releaseDeploymentLog1.Start();
            releaseDeploymentLog1.End(true);
            OperationsData.Environment1.RegisterReleaseDeployment(DevelopmentData.Release1);
            session.SaveOrUpdate(releaseDeploymentLog1);

            var releaseDeploymentLog2 = new ReleaseDeploymentLog(DevelopmentData.Release1, OperationsData.Environment2, UsersData.User1);
            releaseDeploymentLog2.Start();
            releaseDeploymentLog2.End(true);
            OperationsData.Environment2.RegisterReleaseDeployment(DevelopmentData.Release1);
            session.SaveOrUpdate(releaseDeploymentLog2);

            var releaseDeploymentLog3 = new ReleaseDeploymentLog(DevelopmentData.Release2, OperationsData.Environment2, UsersData.User2);
            releaseDeploymentLog3.Start();
            session.SaveOrUpdate(releaseDeploymentLog3);

            AuditData = new AuditDataRepository(releaseDeploymentLog1, releaseDeploymentLog2, releaseDeploymentLog3);
        }

        private void SetUpDefinition(IComponent component)
        {
            component.ActivationDeactivationDefinition.ActivationTemplate = InfrastructureData.ActivationTemplate;
            component.ActivationDeactivationDefinition.DeactivationTemplate = InfrastructureData.DeactivationTemplate;
            component.ActivationDeactivationDefinition.GetDefaultParameterValue(InfrastructureData.ActivationTemplateBooleanParameter).BooleanValue = true;
            component.ActivationDeactivationDefinition.GetDefaultParameterValue(InfrastructureData.ActivationTemplateDateTimeParameter).DateTimeValue = new System.DateTime(2011, 06, 01);
            component.ActivationDeactivationDefinition.GetDefaultParameterValue(InfrastructureData.DeactivationTemplateDecimalParameter).DecimalValue = 1.23m;
            component.ActivationDeactivationDefinition.GetDefaultParameterValue(InfrastructureData.DeactivationTemplateIntegerParameter).IntegerValue = 123;
            component.ActivationDeactivationDefinition.AddEnvironmentParameterValue(InfrastructureData.ActivationTemplateBooleanParameter, OperationsData.Environment1).BooleanValue = false;
            component.ActivationDeactivationDefinition.AddEnvironmentParameterValue(InfrastructureData.ActivationTemplateDateTimeParameter, OperationsData.Environment1).DateTimeValue = new System.DateTime(2011, 12, 21);
            component.ActivationDeactivationDefinition.AddEnvironmentParameterValue(InfrastructureData.DeactivationTemplateDecimalParameter, OperationsData.Environment1).DecimalValue = 3.21m;
            component.ActivationDeactivationDefinition.AddEnvironmentParameterValue(InfrastructureData.DeactivationTemplateIntegerParameter, OperationsData.Environment1).IntegerValue = 321;
            component.ActivationDeactivationDefinition.AddEnvironmentParameterValue(InfrastructureData.DeactivationTemplateDecimalParameter, OperationsData.Environment2).DecimalValue = 23.21m;
            component.ActivationDeactivationDefinition.AddEnvironmentParameterValue(InfrastructureData.DeactivationTemplateIntegerParameter, OperationsData.Environment2).IntegerValue = 2321;
        }

        private void SetUpDefinition(IVersion version)
        {
            version.DeploymentDefinition.DeploymentTemplate = InfrastructureData.DeploymentTemplate;
            version.DeploymentDefinition.GetDefaultParameterValue(InfrastructureData.DeploymentTemplateStringParameter).StringValue = "String Value";
            version.DeploymentDefinition.AddEnvironmentParameterValue(InfrastructureData.DeploymentTemplateStringParameter, OperationsData.Environment1).StringValue = "String Parameter for Environment One";
            version.DeploymentDefinition.AddEnvironmentParameterValue(InfrastructureData.DeploymentTemplateStringParameter, OperationsData.Environment2).StringValue = "String Parameter for Environment Two";
            version.DeploymentDefinition.GetDefaultParameterValue(InfrastructureData.DeploymentTemplateUniqueIdentifierParameter).UniqueIdentifierValue = System.Guid.Empty;
        }


        protected override void ConfigureNails(INailsConfigurator nails)
        {
            nails.IoC.Container<NailsFramework.IoC.Unity>()
                 .Persistence.DataMapper<NailsFramework.Persistence.NHibernate>(CID.Domain.Persistence.Configuration.Configure)
                 .UserInterface.Platform<NailsFramework.UserInterface.Mvc>()
                 .Logging.Logger<NailsFramework.Logging.Log4net>()
                 .IoC.Lemming<NailsFramework.UnitOfWork.Session.WebExecutionContext>(x => x.Ignore())
                 .IoC.Lemming<NailsFramework.UnitOfWork.Session.CallExecutionContext>()
                 .InspectAssembly(@"CID.Domain.dll")
                 .InspectAssembly(@"CID.Web.dll")
                 .Initialize();
        }
    }
}
